package com.ziyun.erp.common.utils;

import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.utility.New;
import com.ziyun.erp.modules.goodsMonitor.annotation.CustomAnnotation;
import com.ziyun.erp.modules.goodsMonitor.entity.GoodsMonitorEntity;
import io.renren.common.exception.RRException;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * 商品日志监控工具类
 * @author xueqz
 * @email
 * @date 2018-11-13 09:56:29
 */
public class GoodsMonitorUtils {

    public static Map<String,String> monitor(Object oldObj, Object newObj, Map<String, String> map) throws Exception {
        //获取旧对象字节码
        Class<?> oldClass = oldObj.getClass();
        //获取目标主键值
        Object targetValue = getTargetValue(oldObj);
        //设属性可访问
        Field[] declaredFields = oldClass.getDeclaredFields();
        //截取字节码字符串
        String[] split = oldClass.toString().split("\\.");
        for (Field field : declaredFields) {
            //获取tableField注解
            TableField annotation = field.getAnnotation(TableField.class);
            field.setAccessible(true);
            Object oldValue = field.get(oldObj);
            Object newValue = field.get(newObj);
            //判断,略过不需要记录的字段-->例如不需要存入数据库的字段,该注解也可自定义其值做标记
            //判断,仅无该注解,且TableField.exist不为false才进行记录
            if(!(annotation != null && !annotation.exist())){
                //获得字段名
                String name = field.getName();
                //获得字段类型
                String type = field.getType().getName();
                field.setAccessible(true);
                //bigDecimal类型的String.valueOf会比较精度,前端传过来的数值是舍弃小数的,在此做类型区别判断
                if(type.equals("java.math.BigDecimal") && oldValue != null && newValue != null){
                    //bigDecimal类型使用compareTo忽略进度比较值
                    if(new BigDecimal(String.valueOf(oldValue)).compareTo(new BigDecimal(String.valueOf(newValue))) != 0){
                        map.put(split[split.length-1]+":"+ targetValue +"-"+name, oldValue + "-->" + newValue);
                    }
                }else if(!String.valueOf(oldValue).equals(String.valueOf(newValue))){
                    //若前后值不一样,则存入数据库-->split[split.length-1]是取字节码最后一节作为标识,即类名
                    map.put(split[split.length-1]+":"+ targetValue +"-"+name, oldValue + "-->" + newValue);
                }
            }
            //自定义注解类CustomAnnotation,其值recursionFlag用以判断是否含有一对一或一对多关联关系
            CustomAnnotation customAnnotation = field.getAnnotation(CustomAnnotation.class);
            //判断递归标识
            if(customAnnotation != null && customAnnotation.recursionFlag()){
                //需要递归
                //获得字段上的所有泛型字节码对象
                Class<?>[] classes = getFieldParameterizedType(field);
                if(classes != null && classes.length > 0){
                    //一对多关系,需要找到主键相同的,进行递归写入
                    List oldList = JsonUtils.string2Collection(com.utility.JsonUtils.object2String(oldValue), List.class, classes[0]);
                    List newList = JsonUtils.string2Collection(com.utility.JsonUtils.object2String(newValue), List.class, classes[0]);
                    if(oldList != null){
                        for (Object oldObject : oldList) {
                            //获取到object的主键
                            Object oldClassValue = getTargetValue(oldObject);
                            for (Object newObject : newList) {
                                Object newClassValue = getTargetValue(newObject);
                                //这里做的操作是-->在新旧list集合中,找到主键对应的两个实体类放入比较
                                if(String.valueOf(oldClassValue).equals(String.valueOf(newClassValue))){
                                    //递归
                                    monitor(oldObject,newObject,map);
                                }
                            }
                        }
                    }
                }else{
                    //一对一,直接放入关联关系对象去比较其值
                    Class<?> type = field.getType();
                    Object oldObject = JsonUtils.string2Object(com.utility.JsonUtils.object2String(oldValue), field.getType());
                    Object newObject = JsonUtils.string2Object(com.utility.JsonUtils.object2String(newValue), field.getType());
                    monitor(oldObject,newObject,map);
                }
            }
        }
        return map;
    }

    /**
     * 获取字段的参数字节码对象集
     * @param field
     * @return
     */
    private static Class<?>[] getFieldParameterizedType(Field field) {
        Class<?>[] classes = null;
        Type tp = field.getGenericType();
        if (tp != null && tp instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) tp;
            Type[] types = pt.getActualTypeArguments();
            if (types != null && types.length > 0) {
                classes = new Class<?>[types.length];
                for (int i = 0; i < classes.length; i++) {
                    classes[i] = (Class<?>) types[i];
                }
            }
        }
        return classes;
    }

    /**
     * 获取object的指定注解属性值
     * @param oldObject
     * @return
     * @throws IllegalAccessException
     */
    private static Object getTargetValue(Object oldObject) throws IllegalAccessException {
        //获取对象的所有字段
        Field[] oldSonFields = oldObject.getClass().getDeclaredFields();
        //遍历字段
        for (Field oldSonField : oldSonFields) {
            //设置属性可访问
            oldSonField.setAccessible(true);
            //筛选获取字段上含有TableId的字段-->认定为主键
            Annotation oldSonFieldAnnotation = oldSonField.getAnnotation(TableId.class);
            if(oldSonFieldAnnotation != null){
                //获得这个主键-->返回其值
                return oldSonField.get(oldObject);
            }
        }
        return new RRException("日志记录异常");
    }

    /**
     *组装监控实体类数据
     */
    public static GoodsMonitorEntity  packingData(Object oldObj, Object newObj,Long userId){
        GoodsMonitorEntity goodsMonitorEntity = new GoodsMonitorEntity();
        Map<String, String> monitor = New.hashMap();
        String[] split = oldObj.getClass().toString().split("\\.");
        Object targetValue = null;
        try {
            targetValue = getTargetValue(oldObj);
            monitor = monitor(oldObj, newObj, monitor);
        } catch (Exception e) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            e.printStackTrace(pw);
            monitor.put("日志记录出现异常",sw.toString() + "\r\n");
        }
        if(monitor != null && monitor.size() > 0){
            if(UserUtils.getCurentUser() != null){
                goodsMonitorEntity.setSource("erp");
            }else{
                goodsMonitorEntity.setSource("pop");
            }
            goodsMonitorEntity.setCreateId(userId);
            goodsMonitorEntity.setMessage(JsonUtils.object2String(monitor));
            goodsMonitorEntity.setSdbId(String.valueOf(targetValue));
            goodsMonitorEntity.setType(split[split.length-1]);
        }
        return goodsMonitorEntity;
    }
}
